package com.ntd.study.coding.standards.hjly;

import java.math.BigDecimal;

/**
 * @author liuwenju
 *
 * @date 2021/3/22
 * 华景乐游代码规范学习
 */
public class StudyTravel {

    public static void main(String[] args) {
        switchDemo();
    }

    /**
     * 1. 代码格式规范
     * 1.1 如果大括号内为空， 则简洁的写作{}即可， 大括号中间无需换行和空格
     * |-- 如果是非空代码块
     * |-- (1) 左大括号前不换行
     * |-- (2) 左大括号后换行
     * |-- (3) 右大括号前换行
     * |-- (4) 右大括号后还有 else 等代码则不换行；表示终止的右大括号必须换行
     */
    public static void base() {
        // 1.2 小括号（内侧两边不需要空格）；而左大括号前需要空格
        // 1.3 if/for/while/switch/do 等保留字与括号之间必须加空格
        if (1 == 1) {}
        // 1.4 任何二目，三目运算符的左右两边都需要加一个空格；运算符包括赋值运算符 = 、逻辑运算符 && 、加减乘除符号等。
        int a = 1 + 2;
        boolean b = true & false;
        // 1.5 采用4个空格缩进，禁止使用 tab 字符。
        {
            System.out.println("缩进4个字符");
        }
        // 1.8 单行字符数限制不超过120个，超出需要换行，换行时遵循如下原则
        // |-- 1) 第二行相对第一行缩进4个空格，从第三行开始不再进行缩进
        // |-- 2) 运算符与下文一起换行
        // |-- 3) 方法调用的点符号与下文一起换行
        // |-- 4) 方法调用中的多个参数需要换行时，在逗号后进行
        // |-- 5) 在括号前不要换行
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("java").append("java")
            .append("java")
            .append("java");
        // 1.9 方法参数在定义和传入时，多个参数逗号后必须加空格
        paramsDemo("java", "java", "java");
        // 1.10 IDE 的 text file encoding 设置为 UTF-8；IDE 中文件的换行符使用Unix格式，不要使用 Windows 格式
        // 1.11 在 if/else/for/while/do 语句中必须使用大括号（即使只有一行代码，避免采用单行的编码方式）
        // 1.12 单个方法的总行数不超过80行
        // |-- 1) 除注释之外的方法签名、左右大括号、方法内代码、空行、回车及任何不可见字符的总行数不超过80行
        // |-- 2) 正例：代码逻辑分清红花和绿叶，个性和共性，绿叶逻辑单独出来成为额外方法，使主干代码更加清晰；共性逻辑抽取成共性方法，便于复用和维护
        // 1.13 不同逻辑、不同语义、不同业务代码之间插入一个空行分隔开来以提升可读性（任何情形，没有必要插入多个空行进行隔开）
        // 1.14 代码中严禁使用拼音及中文
        // 1.15 类名使用 UpperCamelCase 风格，但 DO/BO/DTO/VO/AO/PO/UID 等除外
        // 1.16 方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格，必须遵从驼峰形式
        // 1.17 常量命名全部大写，单词中间用下划线隔开，力求语义表达完整清楚，不要嫌名字长
        // 1.18 包名统一使用小写，点分隔符之间有且仅有一个自然语义的英文单词，包名统一使用单数形式，但是类名如果有复数含义，类名可以使用复数形式
        // 1.19 枚举类名带上Enum后缀，枚举成员名称需要全大写，单词间用下划线隔开（枚举其实就是特殊的类，域成员均为常量，且构造方法被默认强制是私有）
        // 1.20 类内方法定义的顺序依次是：公有方法或保护方法 -> 私有方法 -> getter/setter 方法
        // |-- 1) 公有方法是类的调用者和维护者最关心的方法，首屏展示最好；保护方法虽然只是子类关心，也可能是 “模板设计模式” 下的核心方法；
        // |-- 2) 私有方法外部一般不需要特别关心，是一个黑盒实现
        // |-- 3) getter/setter 方法因为承载的信息价值较低所以放在类体最后

        // 2 《避免挖坑踩坑》
        // 2.1 浮点数运算
        floatDemo();
        // 2.2 为了防止精度丢失，禁止使用构造方法 BigDecimal(double) 的方式把 double 值转换为 BigDecimal 对象
        // 禁止如此操作
        BigDecimal bigDecimal = new BigDecimal(1.0f);
        // 推荐
        new BigDecimal("0.1");
        // 2.3 关于基本数据类型与包装数据类型的使用标准
        // |-- 1) 所有的 POJO 类属性必须使用包装数据类型 -) 没有初值是提醒使用者在使用时必须自己显式的进行赋值，任何 NPE 问题，或者入库检查，都由使用者来保证
        // |-- 2) RPC 方法的返回值和参数必须使用包装数据类型
        // |-- 3) 所有的局部变量使用基本数据类型
        // 2.4 使用 entrySet 遍历 Map 集合 KV，而不是 keySet 进行遍历 -) keySet 其实是遍历了两次 -) jdk8，使用 Map.forEach 方法
        // 2.5 利用 Set 元素的唯一的特性，可以快速对一个集合进行去重操作，避免使用 List 的 contains 方法进行遍历、对比、去重操作
        // 2.6 SimpleDateFormat 是线程不安全的，一般不要定义为 static 变量，如果定义为 static，必须加锁，或者使用 DateUtil 工具类 -) jdk8 Instant 代替 Date，LocalDateTime 代替 Calendar，DateTimeFormat 代替 SimpleDateFormat
        // 2.7 当 switch 括号内的变量类型为 String 类型并且此变量为外部参数时，必须先进性 null 判断
        switchDemo();
        // 2.8 在高并发场景中，避免使用 “等于” 判断作为终端或者退出的条件 -) 如果并发控制没有处理好，容易产生等值判断被 “击穿” 的情况，使用大于或者小于的区间判断来代替
        // 3 《写别人能看得懂的代码》
        // 3.1 表达异常的分支时，少用 if-else 方式，只使用 if return
        // 3.2 除常用方法 getter/setter 等外，不要在条件判断中执行其他复杂的语句，讲复杂逻辑判断的结果赋值给一个有意义的布尔变量名，以提高可读性
        // 3.3 参数校验
        // |-- 1) 调用频次低的方法
        // |-- 2) 执行时间开销很大的方法。此情形中，参数校验的时间几乎可以忽略不计，但如果因为参数错误导致中间执行退回，或者错误，那得不偿失
        // |-- 3) 需要极高稳定性和可用性的方法
        // |-- 4) 对外提供的开发接口，不管是 RPC/API/HTTP 接口
        // |-- 5) 敏感权限入口
        // 3.4 用户敏感数据禁止直接展示，必须对展示数据进行脱敏 -) 例：手机号隐藏中间4位
        // 3.5 方法返回值可以为 null，必须添加充分注释
        // 3.6 防止 NPE，是程序员的基本修养，注意 NPE 产生的场景
        // |-- 1) 返回类型为基本类型，值却为 null
        // |-- 2) 数据库的查询结果可能为 null
        // |-- 3) 集合里的元素即使 isNotEmpty，取出的数据元素也可能为 null
        // |-- 4) 远程调用返回对象时，一律要求进行空指针判断
        // |-- 5) 级联调用，最易产生 NPE，推荐使用 JDK8 的 Optional 来防止 NPE 的问题
        // 3.7 避免出现重复的代码，即 DRY 原则
        // 4 《注释规范》
        // 4.1 类、类属性、类方法的注释必须使用 javadoc 规范，使用 /** 内容 */ 格式，不得使用 // xxx 方式
        // 4.2 所有的抽象方法（包括接口中的方法）必须使用 javadoc 注释、除了返回值、参数、异常说明外，还必须指出该方法做什么事情，实现什么功能
        // 4.3 所有的类都必须添加创建者和创建日期
        // 4.4 方法内容使用单行注释，在被注释语句上方另起一行， 使用 // 注释，方法内部多行注释使用 /* */ 注释，注意与代码对齐
        // 4.5 所有的枚举类型字段必须要有注释，说明每个数据项的用途
        // 4.6 及时清理不再使用的代码段或者配置信息 -) 对于暂时被注释掉，后续可能会恢复的代码片段，统一使用 /// 来说明注释掉代码的理由
        // 4.7 特殊注释标记，需注意标记人及标记时间，注意及时处理这些标记。
        // |-- 1) 待办事宜：TODO（标记人，标记时间，[预计处理时间]），表示需要实现，但目前还未实现的功能 -) 只能应用于类，接口和方法
        // |-- 2) 不能工作：FIXME（标记人，标记时间，[预计处理时间]），不建议使用，错误需要及时处理
        // 5 《其他代码规范》
        // 5.1 尽可能避免在循环中实现加解密或是查询数据库的操作，解决方法是在循环之前先统一进行耗时操作避免重复
        // 5.2 所有Json序列化方式，统一使用FastJson,不要使用jackson
        // 6 《复杂业务方法的重构》
        // 6.1 较长的方法可以简单遵循以下方法进行重构 -) 由于内存中对list的循环效率极高，多次循环带来的开销可以忽略不记
        // |-- 1) 参数的验证 -) 对于验证很长的方法尽可能将验证的每一项拆分成单独的方法，用语义化的方式描述验证操作。而不是耦合在一个大方法中。循环中的验证可以通过多次循环list的方式拆分开。（循环中不应存在耗时的操作，如加解密，查询数据库）
        // |-- 2) 对业务逻辑中需要查询的数据和配置提前封装到上下文中。（这里使用ThreadLocal）如果上下文数据较多，每一项拆分成一个方法
        // |-- 3) 对业务逻辑的处理根据具体的阶段拆分成阶段的实现方法。每个阶段再拆分成细的步骤流程方法。详细的步骤流程可以内聚到领域中实现代码复用
        // |-- 4) 在finally中最终清理ThreadLocal，防止内存泄露
        // 6.2 重构原则 -) 将实体的方法内聚到领域模型或领域实体中，使用语义化的方式命名，即提高代码可读性，增强了代码的业务表达能力，又避免代码方法过于松散，便于代码复用
        // 7 《编程思想规约》
        // 7.1 注意query对象的特殊性，它是一个大范围的查询对象，根据业务实际情况的复杂度来做聚合区分
        // 7.2

    }



    public static void paramsDemo(String ... args){}

    /**
     * 2.1 浮点之间的等值判断，基本数据类型不能用 == 来判断，包装数据类型不能用 equals 来判断。
     */
    public static void floatDemo() {
        // 反例
        float floatOne = 1.0f - 0.9f;
        float floatTwo = 0.9f - 0.8f;
        System.out.println(floatOne == floatTwo);
        System.out.println(Float.valueOf(floatOne).equals(Float.valueOf(floatTwo)));
        // 正例 - 指定一个误差范围，两个浮点数的差值在此范围之内，则认为是相等的
        System.out.println(Math.abs(floatOne - floatTwo) < 1e-6f);
        // 使用 BigDecimal 来定义值，在进行浮点数的运算操作
        System.out.println(new BigDecimal("1.0").subtract(new BigDecimal("0.9")).equals(new BigDecimal("0.1")));
    }

    /**
     * switch Demo 演示
     */
    private static void switchDemo() {
        String str = null;
        switch (str) {
            case "java":
                System.out.println("java");
                break;
            default:
                System.out.println("default");
        }
    }
}
